package com.google.code.pdfbrick.merger;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import com.google.code.pdfbrick.engine.FileOperator;
import com.google.code.pdfbrick.engine.PDFOperator;
import com.google.code.pdfbrick.engine.base.Validatable;
import com.google.code.pdfbrick.engine.command.FilePageRange;
import com.google.code.pdfbrick.engine.command.PageRange;
import com.google.code.pdfbrick.engine.command.ParsableCommand;
import com.google.code.pdfbrick.engine.exception.CommandExecuteException;
import com.google.code.pdfbrick.engine.exception.CommandParseException;
import com.google.code.pdfbrick.engine.exception.CommandValidateException;
import com.google.code.pdfbrick.engine.exception.PDFOperationException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfReader;

/**
 * @author Johny Huang
 * @date 2009-3-7
 */
public class MergeCommand implements ParsableCommand,Validatable {

	private static final Log log = LogFactory.getLog(MergeCommand.class);

	private static final MergeCommandParser PARSER=new MergeCommandParser();	
	private String[] args;
	private MergeCommandParams params;

	@SuppressWarnings("unused")
	private MergeCommand() {
	}
	
	public MergeCommand(final String... args) {
		this.args = args;
	}

	protected void build() {
		try {
			if (params == null) {
				parse();
			}
			validate();
			execute();
			} 
			catch (CommandParseException ex) {
				log.debug(ex.getMessage(), ex);
			}
			catch (CommandValidateException ex) {
				log.debug(ex.getMessage(), ex);
			}
			catch (CommandExecuteException ex) {
				log.debug(ex.getMessage(), ex);
			}
	}

	public void parse(){
		params = PARSER.parse(args);
	}

	public void validate(){
		if(params==null){
			throw new NullPointerException("Can not get a valid input after parse!");
		}
		if(FileOperator.isExistFile(params.getDesFilePath())){
			throw new CommandValidateException("DesFile already exists "+params.getDesFilePath());
		}
		List<FilePageRange> src=params.getSrcFilePageRanges();
		for(FilePageRange fpr:src){
			if(!FileOperator.isExistFile(fpr.getFilePath())){
				throw new CommandValidateException("Source File doesn't exist or it's an invalid file path "+fpr.getFilePath());
			}
		}		
	}

	public void run() {
		build();
	}

	public void execute() throws CommandExecuteException {
		Document document = new Document();
		PdfReader reader = null;
		try {
			FileOperator.createFile(params.getDesFilePath());
			PdfCopy copy = new PdfCopy(document, new BufferedOutputStream(
					new FileOutputStream(params.getDesFilePath())));
			document.open();

			// Merge all pages of srcFiles to desFile
			List<FilePageRange> pgrList = params.getSrcFilePageRanges();
			PDFOperator pdfOpt = new PDFOperator();
			for (FilePageRange range : pgrList) {
				reader = new PdfReader(range.getFilePath());
				List<PageRange> prList = range.getPageRangeList();

				if (range.isAllPages()) {
					// Reader reads all the page and copy to PdfCopy.
					pdfOpt.extractAll(copy, reader);
				} else {
					for (PageRange pr : prList) {
						pdfOpt.extract(copy, reader, pr.getStartIndex(), pr
								.getEndIndex());
					}
				}
				reader.close();
			}
		} catch (IOException ex) {
			throw new CommandExecuteException(ex.getMessage(),ex);
		} catch (DocumentException ex) {
			throw new CommandExecuteException(ex.getMessage(),ex);
		} catch(PDFOperationException ex){
			throw new CommandExecuteException(ex.getMessage(),ex);
		} finally {
			if (reader != null) {
				reader.close();
			}
			document.close();
		}

	}

	public void setParams(MergeCommandParams params) {
		this.params = params;
	}
}
